using System;
using System.ComponentModel;
using System.Collections.Generic;
using MSOutlook = Microsoft.Office.Interop.Outlook;
using MSOffice = Microsoft.Office.Core;
using Microsoft.Office.Tools.Outlook;

namespace Microsoft.Sample.Outlook.Utility
{
	/// <summary>
	/// Utility class to manage launching custom forms(inspecter) when outlook items are opened instead the default Outlook form
	/// </summary>
    public sealed class CustomItemManager
    {
        #region Instance
        MSOutlook.Inspectors _inspectors;
		Dictionary<string, Type> _mapFolderCustomForm = new Dictionary<string, Type>();
		Dictionary<string, CancelEventHandler> _mapFolderWritingHandler = new Dictionary<string, CancelEventHandler>();
        Dictionary<string, CancelEventHandler> _mapFolderDeletingHandler = new Dictionary<string, CancelEventHandler>();
        //Ribbon Instance
        internal static List<Ribbon.OutlookInspector> _windows;       // List of tracked inspector windows
        MSOffice.IRibbonUI _ribbon;
        #endregion


        #region Constructure
        /// <summary>
		/// Create an instance of the Custom Form Manager
		/// </summary>
		/// <param name="app"></param>
		public CustomItemManager(Microsoft.Office.Tools.Outlook.OutlookAddIn app)
			
		{
		}
        
		/// <summary>
		/// Create an instance of the Custom Form Manager
		/// </summary>
		/// <param name="app"></param>
		public CustomItemManager(MSOutlook.Application app, MSOffice.IRibbonUI ribbon)
		{
            _ribbon = ribbon;
            _windows = new List<Ribbon.OutlookInspector>();
			_inspectors = app.Inspectors;
			_inspectors.NewInspector += new MSOutlook.InspectorsEvents_NewInspectorEventHandler(OnNewInspector);
        }
        #endregion


        #region Event Handler
        public void Shutdown()
        {
            _mapFolderCustomForm.Clear();
            _mapFolderWritingHandler.Clear();
            _mapFolderDeletingHandler.Clear(); //Add code
            _inspectors.NewInspector -= new MSOutlook.InspectorsEvents_NewInspectorEventHandler(OnNewInspector);
        }

		/// <summary>
		/// Handle the New Inspector event. If this is a new item being created in a folder with a registered 
		/// custom form, cancel the new inspector (via the CancelItemOpen event handler) and launch
		/// the registered custom form
		/// </summary>
		/// <param name="inspector"></param>
		void OnNewInspector(MSOutlook.Inspector inspector)
		{
			ItemAdapter item = new ItemAdapter((MSOutlook.ItemEvents_10_Event)inspector.CurrentItem);
			
			string folderID;
            
			if (item.EntryID == null) //When create new item
				folderID = inspector.Application.ActiveExplorer().CurrentFolder.EntryID;
			else //When open exising item
			{
				//TODO: Outlook apparently doesn't update the item parent if the item 
				//is dragged and dropped into another folder. thus, this mechanism for
				//determing to launch the custom form is flawed

				MSOutlook.MAPIFolder folder = (MSOutlook.MAPIFolder)item.Parent;
				folderID = folder.EntryID;
			}

            //Custom form or outlook default
			if (_mapFolderCustomForm.ContainsKey(folderID)) //Is folder load custom form
			{
				item.Opening += new CancelEventHandler(CancelItemOpen);
				CreateCustomForm(item, _mapFolderCustomForm[folderID]);
			}
			else if (_mapFolderWritingHandler.ContainsKey(folderID)) //Is folder load outlook default form
			{
				item.Writing += _mapFolderWritingHandler[folderID];
                item.Deleting += _mapFolderDeletingHandler[folderID]; //Add code

				CancelEventHandler OnClose = null;
				OnClose = delegate(object sender, CancelEventArgs e)
				{
					item.Writing -= _mapFolderWritingHandler[folderID];
                    item.Deleting -= _mapFolderDeletingHandler[folderID];
					item.Closing -= OnClose;
				};
				item.Closing += OnClose;
			}

            //Create Ribbon Add in 
            //Personal Contact Ribbon
            if (item.Class == MSOutlook.OlObjectClass.olContact)
            {
                // Check to see if this is a new window 
                // we don't already track
                Ribbon.OutlookInspector existingWindow =
                    FindOutlookInspector(item);
                // If the m_Windows collection does not 
                // have a window for this Inspector,
                // we should add it to m_Windows
                if (existingWindow == null)
                {
                    Ribbon.OutlookInspector window = new Ribbon.OutlookInspector(inspector);
                    window.Close += new EventHandler(WrappedWindow_Close);
                    window.InvalidateControl += new EventHandler<
                        Ribbon.OutlookInspector.InvalidateEventArgs>(
                        WrappedWindow_InvalidateControl);
                    _windows.Add(window);
                }
            }

            //Peronal Appointment Ribbon
            if (item.Class == MSOutlook.OlObjectClass.olAppointment)
            { 
                
            }
		}

        void WrappedWindow_InvalidateControl(object sender,
          Ribbon.OutlookInspector.InvalidateEventArgs e)
        {
            if (_ribbon != null)
            {
                _ribbon.InvalidateControl(e.ControlID);
            }
        }

        void WrappedWindow_Close(object sender, EventArgs e)
        {
            Ribbon.OutlookInspector window = (Ribbon.OutlookInspector)sender;
            window.Close -= new EventHandler(WrappedWindow_Close);
            _windows.Remove(window);
        }

		/// <summary>
		/// Event handler to cancel the new inspector window
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		void CancelItemOpen(object sender, CancelEventArgs e)
		{
			ItemAdapter adapt = (ItemAdapter)sender;
			adapt.Opening -= new CancelEventHandler(CancelItemOpen);

			e.Cancel = true;
        }
        #endregion


        #region Method
        /// <summary>
		/// Helper function to create a new instance of a custom form
		/// </summary>
		/// <param name="adapter"></param>
		/// <param name="formType"></param>
		void CreateCustomForm(ItemAdapter adapter, Type formType)
		{
			System.Reflection.ConstructorInfo ci = formType.GetConstructor(new Type[] { typeof(ItemAdapter) });
			if (ci == null)
				throw new ApplicationException("Form types must have a constructor that takes an ItemAdapter parameter");

			System.Windows.Forms.Form form = (System.Windows.Forms.Form)ci.Invoke(new object[] { adapter });
			form.Show();
		}

		/// <summary>
		/// Register a custom form for a specific folder. 
		/// </summary>
		/// <param name="folder"></param>
		/// <param name="formType"></param>
		public void RegisterCustomForm(MSOutlook.MAPIFolder folder, Type formType)
		{
			if (folder == null)
				throw new ArgumentNullException("Item parameter can't be null");

			if (formType == null)
				throw new ArgumentNullException("formType parameter can't be null");

			//TODO: Check to make sure formType inherits from System.Windows.Forms.Form
			//TODO: Ensure formtype has a constructor that takes an ItemAdapter

			_mapFolderCustomForm.Add(folder.EntryID, formType);
		}

		public void RegisterWriteHandler(MSOutlook.MAPIFolder folder, CancelEventHandler writeHandler)
		{
			if (folder == null)
				throw new ArgumentNullException("Item parameter can't be null");

			if (writeHandler == null)
				throw new ArgumentNullException("formType parameter can't be null");

			_mapFolderWritingHandler.Add(folder.EntryID, writeHandler);
		}

        public void RegisterDeleteHandler(MSOutlook.MAPIFolder folder, CancelEventHandler deleteHandler)
        {
            if (folder == null)
                throw new ArgumentNullException("Item parameter can't be null");

            if (deleteHandler == null)
                throw new ArgumentNullException("formType parameter can't be null");
            _mapFolderDeletingHandler.Add(folder.EntryID, deleteHandler);
        }

        /// <summary>
        /// Looks up the window wrapper for a given window object
        /// </summary>
        /// <param name="window">An outlook inspector window</param>
        /// <returns></returns>
        public static Ribbon.OutlookInspector FindOutlookInspector(object window)
        {
            foreach (Utility.Ribbon.OutlookInspector inspector in _windows)
            {
                if (inspector.Window == window)
                {
                    return inspector;
                }
            }
            return null;
        }
        #endregion
    }
}
